package com.crl.nfp.filter;

import com.crl.nfp.log.model.LogModel;
import com.crl.nfp.log.service.LogService;
import com.crl.nfp.util.Params;
import com.crl.nfp.util.RequestUtil;
import com.crl.nfp.util.ZuulConstUtil;
import com.cars.util.global.GlobalReturnCode;
import com.cars.util.json.JsonResult;
import com.cars.util.json.JsonUtil;
import com.cars.util.jwt.JwtUtil;
import com.cars.util.string.StringUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.*;

/**
 * 响应日志处理器
 *
 * @author 宋长军
 * @date 2020/4/7 14:57
 */
public class ResponseLogFilter extends ZuulFilter {

    @Autowired
    private LogService logService;

    private final Logger LOGGER = LoggerFactory.getLogger("apiLog");

    @Override
    public String filterType() {
        return POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return SEND_RESPONSE_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        return requestContext.sendZuulResponse();
    }

    @Override
    public Object run() {

        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest httpRequest = requestContext.getRequest();
        int httpResponseCode = requestContext.getResponseStatusCode();

        try {
            LogModel logModel = new LogModel();
            setType(httpRequest, logModel);
            logModel.setServiceName((String) requestContext.get(SERVICE_ID_KEY));
            logModel.setClientIp(StringUtil.getIp(httpRequest));
            logModel.setRequestDate((Date) requestContext.get(ZuulConstUtil.REQUEST_DATE));

            // 请求与响应
            String requestParams = RequestUtil.getRequestParams(httpRequest), responseResult = "";
            if (HttpStatus.OK.value() == httpResponseCode) {

                logModel.setRequestParam(requestParams);
                logModel.setPath(httpRequest.getRequestURI());
                RequestUtil.setTimeConsuming(logModel);
                LOGGER.info("INFO-请求日志：" + logModel.toSuccessString());
                logService.insertLog(logModel);
            } else {

                try {
                    // SendResponseFilter responseBody只关注null即可
                    String responseBody = requestContext.getResponseBody();
                    if (responseBody != null) {
                        responseResult = responseBody;
                    } else {
                        ByteArrayOutputStream byteArrayOutputStream = cloneInputStream(requestContext.getResponseDataStream());
                        if (byteArrayOutputStream != null) {
                            InputStream copyResponseDataStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                            InputStream copyResponseDataStream1 = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                            responseResult = StreamUtils.copyToString(copyResponseDataStream1, StandardCharsets.UTF_8);
                            requestContext.setResponseDataStream(copyResponseDataStream);
                        }
                    }
                } catch (Exception e) {
                    LOGGER.error("响应数据转换异常！", e);
                }
                Params params = new Params(requestParams);
                if (!StringUtils.isEmpty(responseResult)) {
                    // returnCode
                    try {
                        JsonResult jsonResult = JsonUtil.toObject(responseResult, JsonResult.class);
                        logModel.setPath(httpRequest.getRequestURI());
                        logModel.setReturnCode(jsonResult.getReturnCode());
                    } catch (Exception e) {
                        logModel.setPath((String) requestContext.get(ZuulConstUtil.REQUEST_PATH));
                        logModel.setReturnCode(GlobalReturnCode.SERVER_ERROR.getReturnCode());
                        LOGGER.error("解析响应结果异常", e);
                    }
                } else {
                    responseResult = "响应数据转换异常";
                }
                params.setResponse(responseResult);
                logModel.setErrorType(1);
                logModel.setParamsStr(JsonUtil.toStr(params));
                logModel.setParams(JsonUtil.toStr(params).getBytes("GBK"));
                RequestUtil.setTimeConsuming(logModel);
                LOGGER.info("INFO-请求日志：" + logModel.toErrorString());
                logService.insertErrorLog(logModel);
            }
        } catch (Exception e) {
            LOGGER.error("ERROR-网关解析日志异常！", e);
        }
        return null;
    }

    /**
     * CLONE inputStream
     *
     * @param inputStream inputStream
     * @return ByteArrayOutputStream
     */
    private ByteArrayOutputStream cloneInputStream(InputStream inputStream) {

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        try {
            while ((length = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, length);
            }
            byteArrayOutputStream.flush();
            byteArrayOutputStream.close();
            return byteArrayOutputStream;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 设置请求类型
     *
     * @param httpRequest 请求
     * @param logModel    日志
     */
    private void setType(HttpServletRequest httpRequest, LogModel logModel) {
        String appid = httpRequest.getHeader(ZuulConstUtil.APPID_AUTH);
        String token = httpRequest.getHeader(ZuulConstUtil.PASSWORD_AUTH);

        // APPID 模式
        if (!StringUtils.isEmpty(appid)) {
            logModel.setAuthType(ZuulConstUtil.APPID_AUTH_TYPE);
            logModel.setOwner(appid);
        }
        // PASSWORD 模式
        else if (!StringUtils.isEmpty(token)) {
            logModel.setAuthType(ZuulConstUtil.PASSWORD_AUTH_TYPE);
            logModel.setOwner(JwtUtil.getJWTOwner(token));
        }
        // 无需权限的请求
        else {
            logModel.setAuthType(ZuulConstUtil.WHITHOU_AUTH_TYPE);
            logModel.setOwner(ZuulConstUtil.WHITHOUT_AUTH_TYPE_NAME);
        }
    }
}